#include "libcomm.h"

ThreadSignal::ThreadSignal(bool bInitialState)
#ifdef COMM_MSVC
	:m_hSignal(NULL)
#else
	: m_bSignalState(bInitialState)
#endif
{
#ifdef COMM_MSVC
	MYLIB_ASSERT_ERRNO(NULL != (m_hSignal = CreateEvent(NULL,TRUE,(bInitialState?TRUE:FALSE),NULL)));
#else
	pthread_mutexattr_t attr;
	MYLIB_ASSERT_VALUE(0, pthread_mutexattr_init(&attr));
	MYLIB_ASSERT_VALUE(0, pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE));
#if defined(__CYGWIN__) || defined(__ANDROID__) || defined(__OHOS__)
	MYLIB_ASSERT_VALUE(0, pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE));
#else
	MYLIB_ASSERT_VALUE(0, pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP));
#endif
	MYLIB_ASSERT_VALUE(0, pthread_mutex_init(&m_mutex, &attr));
	MYLIB_ASSERT_VALUE(0, pthread_mutexattr_destroy(&attr));
	
	MYLIB_ASSERT_VALUE(0, pthread_cond_init(&m_cond, NULL));
#endif
}

ThreadSignal::~ThreadSignal(void)
{
#ifdef COMM_MSVC
	if(NULL != m_hSignal)
	{
		CloseHandle(m_hSignal);
		m_hSignal = NULL;
	}
#else
	MYLIB_ASSERT_VALUE(0, pthread_mutex_destroy(&m_mutex));
	MYLIB_ASSERT_VALUE(0, pthread_cond_destroy(&m_cond));
#endif
}

void ThreadSignal::SetSignal()
{
#ifdef COMM_MSVC

	MYLIB_ASSERT_ERRNO(SetEvent(m_hSignal));
	
#else

	MYLIB_ASSERT_VALUE(0, pthread_mutex_lock(&m_mutex));
	{
		m_bSignalState = true;
		MYLIB_ASSERT_VALUE(0, pthread_cond_broadcast(&m_cond));
	}
	MYLIB_ASSERT_VALUE(0, pthread_mutex_unlock(&m_mutex));

#endif
}

void ThreadSignal::ResetSignal()
{
#ifdef COMM_MSVC

	MYLIB_ASSERT_ERRNO(ResetEvent(m_hSignal));

#else

	MYLIB_ASSERT_VALUE(0, pthread_mutex_lock(&m_mutex));
	{
		m_bSignalState = false;
	}
	MYLIB_ASSERT_VALUE(0, pthread_mutex_unlock(&m_mutex));

#endif
}

#ifdef __GNUC__
static void __wait_cleanup(void *arg)
{    
	LOGWARN("thread(%d) was canceled while ThreadSignal::WaitSignal(), then release mutex.",Thread::Id());
	pthread_mutex_t* mutex = (pthread_mutex_t*)arg;
	MYLIB_ASSERT_VALUE(0, pthread_mutex_unlock(mutex));
}
#endif

bool ThreadSignal::WaitSignal(unsigned int timeoutMs)
{
#ifdef COMM_MSVC

	DWORD dwEvent = WaitForSingleObject(m_hSignal,timeoutMs); 
	switch (dwEvent) 
	{ 
	case WAIT_OBJECT_0: 
		return true;
	case WAIT_TIMEOUT: 
		return false;
	default: 
		break;
	}
	LOGFUNCERROR(WaitForSingleObject);
	return false;

#else

	int ret = 0;
	timespec ts = { 0 };
	if (timeoutMs != -1)
		ts = Timer::MakeTimespec(timeoutMs);
	pthread_cleanup_push(__wait_cleanup, (void*)&m_mutex);
	{
		MYLIB_ASSERT_VALUE(0, pthread_mutex_lock(&m_mutex));
		while(!m_bSignalState)
		{ 
			if (-1 == timeoutMs)
			{
				ret = pthread_cond_wait(&m_cond, &m_mutex);
			}
			else
			{
				ret = pthread_cond_timedwait(&m_cond, &m_mutex, &ts);
			}

			if (0 != ret && EINTR == errno)
			{
				LOGFUNCERROR(pthread_cond_timedwait);
				continue;
			}

			break;
		} 
		MYLIB_ASSERT_VALUE(0, pthread_mutex_unlock(&m_mutex));
	}
	pthread_cleanup_pop(0);

	if (0 == ret)
		return true;
	else if (ETIMEDOUT == ret)
		return false;
	LOGFUNCERRORNO(pthread_cond_timedwait, ret);
	return false;

#endif

}

bool ThreadSignal::IsSignaled()
{
	return WaitSignal(0);
}
